﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;

namespace MaiYao
{
    public class 内存读取器 : IDisposable
    {
        private IntPtr _进程句柄 = IntPtr.Zero;
        private int _进程ID;
        private IntPtr _主模块基址; // 主模块基址（默认基准）
        private const uint 进程权限 = 0x0010 | 0x0400; // 读取内存 + 查询信息权限
        private const int 最大字符串长度 = 1024;
        private const int 简体中文代码页 = 936;

        // Windows API声明（新增模块枚举相关）
        [DllImport("user32.dll", SetLastError = true)]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool ReadProcessMemory(
            IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesRead);

        [DllImport("kernel32.dll")]
        private static extern bool CloseHandle(IntPtr hObject);

        // 新增：枚举进程模块和获取模块名的API
        [DllImport("psapi.dll", SetLastError = true)]
        private static extern bool EnumProcessModules(
            IntPtr hProcess, IntPtr[] lphModule, uint cb, out uint lpcbNeeded);

        [DllImport("psapi.dll", CharSet = CharSet.Auto)]
        private static extern uint GetModuleBaseName(
            IntPtr hProcess, IntPtr hModule, StringBuilder lpBaseName, uint nSize);

        /// <summary>
        /// 构造函数：保持原有逻辑，仅关联进程并获取主模块基址
        /// </summary>
        public 内存读取器(IntPtr 窗口句柄)
        {
            if (窗口句柄 == IntPtr.Zero)
                throw new ArgumentException("窗口句柄不能为空", nameof(窗口句柄));

            // 获取进程ID
            GetWindowThreadProcessId(窗口句柄, out _进程ID);
            if (_进程ID == 0)
                throw new Exception("无法获取进程ID");

            // 打开进程
            _进程句柄 = OpenProcess(进程权限, false, _进程ID);
            if (_进程句柄 == IntPtr.Zero)
                throw new Exception($"打开进程失败，错误码：{Marshal.GetLastWin32Error()}");

            // 获取主模块基址（作为默认基址）
            _主模块基址 = 获取主模块基址();
            if (_主模块基址 == IntPtr.Zero)
                throw new Exception("无法获取主模块基址");
        }

        #region 字符串读取（新增模块名参数）
        /// <summary>
        /// 读取偏移表达式指向的中文字符串（GBK编码）
        /// </summary>
        /// <param name="偏移表达式">多级偏移表达式（如"000AE164->10+20"）</param>
        /// <param name="模块名">指定模块（如"Engine.dll"，为空则使用主模块）</param>
        public string 读取字符串(string 偏移表达式, string 模块名 = null)
        {
            // 根据模块名获取基准基址（核心修改）
            IntPtr 基准基址 = string.IsNullOrWhiteSpace(模块名) ? _主模块基址 : 获取模块基址(模块名);
            IntPtr 指针地址 = 解析偏移表达式(偏移表达式, 基准基址);
            IntPtr 目标地址 = 读取32位指针(指针地址);
            return 读取简体中文字符串(目标地址);
        }

        private string 读取简体中文字符串(IntPtr 地址)
        {
            byte[] 缓冲区 = new byte[最大字符串长度 + 1];
            if (!ReadProcessMemory(_进程句柄, 地址, 缓冲区, 最大字符串长度, out int 实际读取) || 实际读取 == 0)
                throw new Exception($"读取字符串失败，地址：0x{地址:X8}，错误：{Marshal.GetLastWin32Error()}");

            int 有效长度 = 0;
            while (有效长度 < 实际读取 && 有效长度 < 最大字符串长度 && 缓冲区[有效长度] != 0)
                有效长度++;

            return Encoding.GetEncoding(简体中文代码页).GetString(缓冲区, 0, 有效长度);
        }
        #endregion

        #region 32位整数读取（新增模块名参数）
        /// <summary>
        /// 读取偏移表达式指向的32位有符号整数（int）
        /// </summary>
        /// <param name="偏移表达式">多级偏移表达式</param>
        /// <param name="模块名">指定模块（如"Engine.dll"，为空则使用主模块）</param>
        public int 读取32位整数(string 偏移表达式, string 模块名 = null)
        {
            IntPtr 基准基址 = string.IsNullOrWhiteSpace(模块名) ? _主模块基址 : 获取模块基址(模块名);
            IntPtr 目标地址 = 解析偏移表达式(偏移表达式, 基准基址);
            return 读取32位整数直接(目标地址);
        }

        /// <summary>
        /// 读取偏移表达式指向的32位无符号整数（uint）
        /// </summary>
        /// <param name="偏移表达式">多级偏移表达式</param>
        /// <param name="模块名">指定模块（如"Engine.dll"，为空则使用主模块）</param>
        public uint 读取32位无符号整数(string 偏移表达式, string 模块名 = null)
        {
            IntPtr 基准基址 = string.IsNullOrWhiteSpace(模块名) ? _主模块基址 : 获取模块基址(模块名);
            IntPtr 目标地址 = 解析偏移表达式(偏移表达式, 基准基址);
            return (uint)读取32位整数直接(目标地址);
        }

        private int 读取32位整数直接(IntPtr 地址)
        {
            byte[] 缓冲区 = new byte[4];
            if (!ReadProcessMemory(_进程句柄, 地址, 缓冲区, 4, out int 实际读取) || 实际读取 != 4)
                throw new Exception($"读取32位整数失败，地址：0x{地址:X8}，错误：{Marshal.GetLastWin32Error()}");

            return BitConverter.ToInt32(缓冲区, 0);
        }
        #endregion
        // 新增：8字节有符号整数（long）读取
        #region 8字节整数读取
        /// <summary>
        /// 读取偏移表达式指向的8字节有符号整数（long）
        /// </summary>
        /// <param name="偏移表达式">多级偏移表达式</param>
        /// <param name="模块名">指定模块（如"Engine.dll"，为空则使用主模块）</param>
        public long 读取64位整数(string 偏移表达式, string 模块名 = null)
        {
            IntPtr 基准基址 = string.IsNullOrWhiteSpace(模块名) ? _主模块基址 : 获取模块基址(模块名);
            IntPtr 目标地址 = 解析偏移表达式(偏移表达式, 基准基址);
            return 读取64位整数直接(目标地址);
        }

        /// <summary>
        /// 直接读取指定地址的8字节有符号整数（内部调用）
        /// </summary>
        private long 读取64位整数直接(IntPtr 地址)
        {
            byte[] 缓冲区 = new byte[8]; // 8字节整数占8字节
            if (!ReadProcessMemory(_进程句柄, 地址, 缓冲区, 8, out int 实际读取) || 实际读取 != 8)
                throw new Exception($"读取64位整数失败，地址：0x{地址:X8}，错误：{Marshal.GetLastWin32Error()}");

            return BitConverter.ToInt64(缓冲区, 0); // 转换为8字节有符号整数
        }
        #endregion

        #region 通用工具方法（调整偏移解析逻辑，支持动态基准基址）
        /// <summary>
        /// 解析偏移表达式（新增基准基址参数，由外部传入）
        /// </summary>
        private IntPtr 解析偏移表达式(string 表达式, IntPtr 基准基址)
        {
            if (string.IsNullOrWhiteSpace(表达式))
                throw new ArgumentException("偏移表达式不能为空", nameof(表达式));

            string[] 指针层级 = 表达式.Split(new[] { "->" }, StringSplitOptions.RemoveEmptyEntries);
            IntPtr 当前地址 = 基准基址; // 基于传入的基准基址计算（核心修改）

            for (int i = 0; i < 指针层级.Length; i++)
            {
                string[] 同层偏移 = 指针层级[i].Split('+');
                IntPtr 层级地址 = 当前地址;

                foreach (string 偏移字符串 in 同层偏移)
                {
                    string trimmed = 偏移字符串.Trim();
                    if (!int.TryParse(trimmed, NumberStyles.HexNumber, null, out int 偏移值))
                        throw new ArgumentException($"无效的偏移格式：{trimmed}，表达式：{表达式}", nameof(表达式));

                    层级地址 = (IntPtr)((long)层级地址 + 偏移值);
                }

                if (i != 指针层级.Length - 1)
                {
                    层级地址 = 读取32位指针(层级地址);
                    if (层级地址 == IntPtr.Zero)
                        throw new InvalidOperationException($"指针指向空地址，层级：{指针层级[i]}，表达式：{表达式}");
                }

                当前地址 = 层级地址;
            }

            return 当前地址;
        }

        private IntPtr 读取32位指针(IntPtr 指针地址)
        {
            byte[] 缓冲区 = new byte[4];
            if (!ReadProcessMemory(_进程句柄, 指针地址, 缓冲区, 4, out int 实际读取) || 实际读取 != 4)
                throw new Exception($"读取指针失败，地址：0x{指针地址:X8}，错误：{Marshal.GetLastWin32Error()}");

            return (IntPtr)BitConverter.ToInt32(缓冲区, 0);
        }

        /// <summary>
        /// 获取主模块基址（原有逻辑保留）
        /// </summary>
        private IntPtr 获取主模块基址()
        {
            using (Process 进程 = Process.GetProcessById(_进程ID))
            {
                return 进程.MainModule?.BaseAddress ?? IntPtr.Zero;
            }
        }

        /// <summary>
        /// 新增：根据模块名获取基址（如"Engine.dll"）
        /// </summary>
        private IntPtr 获取模块基址(string 模块名)
        {
            IntPtr[] 模块句柄列表 = new IntPtr[1024];
            uint 所需字节数;

            // 枚举进程所有模块
            if (!EnumProcessModules(_进程句柄, 模块句柄列表, (uint)(模块句柄列表.Length * IntPtr.Size), out 所需字节数))
                throw new Exception($"枚举模块失败，错误码：{Marshal.GetLastWin32Error()}");

            int 模块数量 = (int)(所需字节数 / (uint)IntPtr.Size);

            // 遍历查找目标模块
            for (int i = 0; i < 模块数量; i++)
            {
                IntPtr 模块句柄 = 模块句柄列表[i];
                StringBuilder 模块名缓冲区 = new StringBuilder(256);

                if (GetModuleBaseName(_进程句柄, 模块句柄, 模块名缓冲区, (uint)模块名缓冲区.Capacity) == 0)
                    continue;

                if (string.Equals(模块名缓冲区.ToString(), 模块名, StringComparison.OrdinalIgnoreCase))
                {
                    return 模块句柄; // 模块句柄即基址
                }
            }

            throw new KeyNotFoundException($"未找到模块：{模块名}");
        }
        #endregion

        // 释放资源
        public void Dispose()
        {
            if (_进程句柄 != IntPtr.Zero)
                CloseHandle(_进程句柄);
            GC.SuppressFinalize(this);
        }
    }
}